﻿/**
 * 功能简介：
 * （1）操作简便、友好的系统界面；
 * （2）设置运行密码：让软件只允许您一人使用。
 * （3）可以点对点进行文件传输；
 * （4）有断点续传的
 * （5）对传输的文件进行加密，进行数字签名；
 * （6）对接收的文件进行解密；
 * （7）提供传输双方的即时交流沟通；（如果用对称密码加密协商密钥）
 * （8）提供传输日志的查阅。
 */

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.IO;
using System.Security;
using System.Security.Cryptography;


namespace Crypt_Tranceiver
{

    public partial class Form_Main : Form
    {
        public delegate void CloseFather();//声明一个委托

        public CloseFather close_father; //定义委托

        private String log_file = "log.log";


        private int msg_seqno;

        public static String local_ip;
        public static int local_listen_port = 10240;

        private String remote;
        private String local;

        private Thread bg_thread;

        public static Mutex tcp_in_mutex;
        public static Mutex tcp_out_mutex;
        public static Mutex status_mutex;

        public static Queue<work_list> tcp_inlist;
        public static Queue<work_list> tcp_outlist;
        public static Queue<work_list> status_inlist;

        private String path_program;
        private String path_temp;
        private String path_key;

        private RSA_file rsa_file;
        private String rsa_key_name;
        private String rsa_key_remote;
        private String remote_rsa_key = null;


        public Form_Main()
        {
            InitializeComponent();

            this.adjust_size();

            msg_seqno = 0;

            tcp_inlist = new Queue<work_list>();
            status_inlist = new Queue<work_list>();
            tcp_in_mutex = new Mutex();
            tcp_outlist = new Queue<work_list>();
            tcp_out_mutex = new Mutex();
            status_mutex = new Mutex();

            bg_thread = new Thread(new ThreadStart(bg_work));
            bg_thread.Start();

            IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
            IPAddress ipAddress;
            for (int i = 0; i != ipHostInfo.AddressList.Length; i++)
            {
                if (!ipHostInfo.AddressList[i].IsIPv6LinkLocal)
                {
                    ipAddress = ipHostInfo.AddressList[i];
                    local_ip = ipAddress.ToString();
                    if (local_ip.IndexOf("169.254") >= 0)
                    {
                        continue;
                    }
                    break;
                }
            }
            

            path_program = System.AppDomain.CurrentDomain.BaseDirectory;
            path_temp = path_program + "Temp";
            path_key = path_program + "Keys";
            rsa_key_name = path_key + "\\my_rsa";
            rsa_key_remote = path_key + "\\remote_rsa";
            rsa_file = new RSA_file();
            if (false == Directory.Exists(path_temp))
            {
                Directory.CreateDirectory(path_temp);
            }
            if (false == Directory.Exists(path_key))
            {
                Directory.CreateDirectory(path_key);
            }
            if ((File.Exists(rsa_key_name + ".public") == false) || (File.Exists(rsa_key_name + ".private") == false))
            {
                rsa_file.GenKey();
                File.WriteAllText(rsa_key_name + ".private", rsa_file.str_Private_Key, Encoding.UTF8);
                File.WriteAllText(rsa_key_name + ".public", rsa_file.str_Public_Key, Encoding.UTF8);
            }
            else
            {
                TextReader fin = new StreamReader(File.Open((rsa_key_name + ".public"), FileMode.Open, FileAccess.Read));
                rsa_file.str_Public_Key = fin.ReadLine();
                fin.Close();
                fin = new StreamReader(File.Open((rsa_key_name + ".private"), FileMode.Open, FileAccess.Read));
                rsa_file.str_Private_Key = fin.ReadLine();
                fin.Close();
            }
            if (File.Exists(rsa_key_remote + ".public"))
            {
                TextReader fin = new StreamReader(File.Open((rsa_key_remote + ".public"), FileMode.Open, FileAccess.Read));
                remote_rsa_key = fin.ReadLine();
                fin.Close();
            }
            else
            {
                MessageBox.Show("请将对方的公钥放在文件夹" + path_key + "下。", "没有找到对方的公钥...", MessageBoxButtons.OK,MessageBoxIcon.Warning);
            }
        }

        /**
         * 动态调整各个控件的大小和位置
         */
        private void adjust_size()
        {

            /*聊天窗口的调整*/
            gb_chat.Width = this.Width * 2 / 3 - 25;
            gb_chat.Height = this.Height - 75;
            gb_chat.Location = new System.Drawing.Point(this.Width / 3 , 25);
            rtb_rec.Width = rtb_rec.Parent.Width - 50;
            rtb_rec.Height = rtb_rec.Parent.Height * 2 / 3 - 100;
            rtb_rec.Location = new System.Drawing.Point( 25,  25);
            rtb_send.Width = rtb_send.Parent.Width - 50;
            rtb_send.Height = rtb_send.Parent.Height * 1 / 3;
            rtb_send.Location = new System.Drawing.Point(25, rtb_rec.Height + 50);
            btn_sendmsg.Location = new System.Drawing.Point(rtb_send.Location.X + rtb_send.Width - btn_sendmsg.Width, rtb_send.Location.Y + rtb_send.Height + 10);
            btn_clearmsg.Location = new System.Drawing.Point(rtb_send.Width / 2, btn_sendmsg.Location.Y);

            /*文件控件的调整*/
            gb_file.Width = this.Width * 1 / 3 - 25;
            gb_file.Height = gb_chat.Height / 2 + 25;
            gb_file.Location = new System.Drawing.Point(25, gb_chat.Location.Y);
            tb_ip.Location = new System.Drawing.Point(label1.Location.X+label1.Width+5, label1.Location.Y - 3);
            tb_ip.Width = gb_file.Width - tb_ip.Location.X - 15;

            tb_file.Width = tb_ip.Width;
            tb_pass1.Width = tb_ip.Width;
            tb_pass2.Width = tb_ip.Width;
            tb_port.Width = tb_ip.Width;
            tb_file.Location = new System.Drawing.Point(tb_ip.Location.X, tb_file.Location.Y);
            tb_pass1.Location = new System.Drawing.Point(tb_ip.Location.X, tb_pass1.Location.Y);
            tb_pass2.Location = new System.Drawing.Point(tb_ip.Location.X, tb_pass2.Location.Y);
            tb_port.Location = new System.Drawing.Point(tb_ip.Location.X, tb_port.Location.Y);

            btn_conn.Width = (gb_file.Width - btn_conn.Location.X - 15 - 15) / 2;
            btn_file.Width = btn_conn.Width;
            btn_file.Location = new System.Drawing.Point(btn_conn.Location.X + btn_conn.Width + 15, btn_file.Location.Y);
            btn_sendfile.Width = btn_conn.Width;
            btn_log.Width = btn_file.Width;
            btn_log.Location = new System.Drawing.Point(btn_file.Location.X, btn_log.Location.Y);
            btn_listen.Width = btn_conn.Width;
            btn_clearlog.Width = btn_conn.Width;
            btn_clearlog.Location = new System.Drawing.Point(btn_file.Location.X, btn_listen.Location.Y);
            
            pb_file.Height = 20;
            pb_file.Location = new System.Drawing.Point(pb_file.Location.X, gb_file.Height - pb_file.Height - 5);
            pb_file.Width = gb_file.Width - 25;
            lb_file.Location = new System.Drawing.Point(lb_file.Location.X, pb_file.Location.Y + 5);

            /*日志控件的调整*/
            gb_log.Width = gb_file.Width;
            gb_log.Height = gb_chat.Height - gb_file.Height - 10;
            gb_log.Location = new System.Drawing.Point(gb_file.Location.X, gb_file.Location.Y + gb_file.Height + 10);
            rtb_log.Width = gb_log.Width - 2 * rtb_log.Location.X;
            rtb_log.Height = gb_log.Height - 2 * rtb_log.Location.Y;
        }

        /**
         * 增加一条log
         */
        private void log_add(String log)
        {
            String s = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff") + ":" + log + "\r\n";
            rtb_log.AppendText(s);
            File.AppendAllText(log_file, s);
        }

        public delegate void MessageReceivedDelegate(string value);
        public void MessageReceived(string value)
        {
            if (this.InvokeRequired)
            {
                MessageReceivedDelegate d = new MessageReceivedDelegate(MessageReceived);
                this.Invoke(d, new object[] { value });
            }
            else
            {
                this.rtb_rec.AppendText(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                this.rtb_rec.AppendText(value);
            }
        }


        private void client_send(TcpClient client, String msg)
        {
            NetworkStream stream = client.GetStream();
            Byte[] data = System.Text.Encoding.UTF8.GetBytes(msg);
            stream.Write(data, 0, data.Length); 

        }

        /** ************************************************************************************************************
         * 主窗体控件
         ************************************************************************************************************ */

        private void Form_Main_SizeChanged(object sender, EventArgs e)
        {
            this.adjust_size();
        }

        private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {

        }

        private void tb_ip_TextChanged(object sender, EventArgs e)
        {
            tb_ip.Text= tb_ip.Text.Trim();
        }

        private void tb_pass1_TextChanged(object sender, EventArgs e)
        {
            if (tb_pass1.Text.Length > 0)
            {
                tb_pass2.Enabled = true;
            }
            else
            {
                tb_pass2.Enabled = false;
            }
            if ((tb_pass1.Text.Length >= 6) && (tb_pass1.Text == tb_pass2.Text) && (connected == true))
            {
                btn_sendfile.Enabled = true;
            }
            else
            {
                btn_sendfile.Enabled = false;
            }
        }

        private void tb_pass2_TextChanged(object sender, EventArgs e)
        {
            if ((tb_pass2.Text.Length >= 6) && (tb_pass1.Text == tb_pass2.Text) && (connected == true))
            {
                btn_sendfile.Enabled = true;
            }
            else
            {
                btn_sendfile.Enabled = false;
            }
        }

        private void btn_conn_Click(object sender, EventArgs e)
        {
            String server;
            int port = 10240, pos;

            pos = tb_ip.Text.Trim().IndexOf(":");
            server = tb_ip.Text.Trim().Remove(pos);
            port = Convert.ToInt32(tb_ip.Text.Trim().Substring(pos+1),10);
            System.Text.RegularExpressions.Regex check = new System.Text.RegularExpressions.Regex(@"^(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9])\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[1-9]|0)\.(25[0-5]|2[0-4][0-9]|[0-1]{1}[0-9]{2}|[1-9]{1}[0-9]{1}|[0-9])$");
            if (!check.IsMatch(server))
            {
                MessageBox.Show("请输入正确的IP地址。");
            }
            else if(btn_conn.Text == "连接")
            {
                tb_ip.ReadOnly = true;
                btn_conn.Enabled = false;
                work_list item = new work_list();
                item.Remote = server + ":" + port;
                item.Oper = work_list.CLIENT_CONNECT;
                item.Req_Res = work_list.DIR_REQ;
                item.Date = server + ":" + port;
                tcp_out_mutex.WaitOne();
                tcp_outlist.Enqueue(item);
                tcp_out_mutex.ReleaseMutex();                
            }
            else if (btn_conn.Text == "断开连接")
            {
                tb_ip.ReadOnly = true;
                btn_conn.Enabled = false;
                work_list item = new work_list();
                item.Remote = server + ":" + port;
                item.Oper = work_list.CMD_BYE;
                item.Req_Res = work_list.DIR_REQ;
                item.Local = local;
                tcp_out_mutex.WaitOne();
                tcp_outlist.Enqueue(item);
                tcp_out_mutex.ReleaseMutex();
            }
        }

        private void btn_file_Click(object sender, EventArgs e)
        {
            openFileDialog.ShowDialog();
            if (openFileDialog.FileName.Length > 0)
            {
                tb_file.Text = openFileDialog.FileName;
            }
        }

        private void Form_Main_FormClosed(object sender, FormClosedEventArgs e)
        {
            bg_thread.Abort();
        }

        /**
         * 定时接收客户端和服务端返回了消息
         */
        private void tim_main_Tick(object sender, EventArgs e)
        {
            
            work_list work_item = null;
            String log;

            /*
             * 状态队列
             */
            while (status_inlist.Count != 0)
            {
                status_mutex.WaitOne();
                if (status_inlist.Count != 0)
                {
                    work_item = null;
                    work_item = status_inlist.Dequeue();
                }
                status_mutex.ReleaseMutex();
                if (work_item == null)
                {
                    break;
                }
                else
                {
                    switch (work_item.Oper)
                    {
                        case work_list.SERVER_START://服务端开始服务
                            if (work_item.Req_Res == work_list.DIR_RES)
                            {
                                btn_listen.Enabled = true;
                                local = work_item.Date;
                                btn_listen.Text = "停止监听";
                                log = "监听端口：" + work_item.Date + "。";
                                log_add(log);
                            }
                            break;
                        case work_list.SERVER_STOP://服务端停止服务
                            if (work_item.Direction == work_list.DIR_IN)
                            {
                                work_item.Direction = work_list.DIR_OUT;
                                work_item.Oper = work_list.CMD_BYE;
                                status_mutex.WaitOne();
                                status_inlist.Enqueue(work_item);
                                status_mutex.ReleaseMutex();
                            }
                            else
                            {
                                btn_listen.Enabled = true;
                                tb_port.Enabled = true;
                                btn_listen.Text = "开始监听";
                                log = "停止监听端口：" + work_item.Date + "。";
                                log_add(log);
                            }
                            break;
                        case work_list.CLIENT_ACCEPT://接受来自于客户端的连接
                            if (work_item.Req_Res == work_list.DIR_REQ)
                            {
                                if (work_item.Error == work_list.ERROR_OK)
                                {
                                    connected = true;
                                    remote = work_item.Date;
                                    log = "接受" + work_item.Date+ "的连接。";
                                    log_add(log);
                                }
                                
                            }
                            break;
                        case work_list.CLIENT_CONNECT:/*连接服务端*/
                            if (work_item.Req_Res == work_list.DIR_REQ)
                            {
                                if (work_item.Error == work_list.ERROR_OK)
                                {
                                    remote = work_item.Date;
                                    log = "连接" + work_item.Date + "。";
                                    btn_conn.Enabled = true;
                                    connected = true;
                                    int pos = client.LocalEndPoint.ToString().IndexOf(":");
                                    local = local_ip + ":" + client.LocalEndPoint.ToString().Substring(pos + 1);
                                    btn_conn.Text = "断开连接";
                                    if ((tb_pass1.Text == tb_pass2.Text) && (tb_pass1.Text.Length > 0))
                                    {
                                        btn_sendfile.Enabled = true;
                                    }
                                    else
                                    {
                                        btn_sendfile.Enabled = false;
                                    }

                                }
                                else
                                {
                                    remote = null;
                                    log = "无法连接" + work_item.Remote + "原因是：" + work_item.Date;
                                    btn_conn.Enabled = true;
                                    tb_ip.ReadOnly = false;
                                    connected = false;
                                    btn_conn.Text = "连接";
                                    btn_sendfile.Enabled = false;
                                }
                                log_add(log);
                            }
                            break;
                        case work_list.CMD_BYE:
                            if (work_item.Direction == work_list.DIR_OUT)
                            {
                                btn_conn.Enabled = true;
                                connected = false;
                                tb_ip.ReadOnly = false;
                                btn_conn.Text = "连接";
                                log = "断开连接" + work_item.Remote + "。";
                                log_add(log);
                                client.Shutdown(SocketShutdown.Both);
                                client.Close();
                            }
                            break;
                        case work_list.FILE_START:
                            if (work_item.Req_Res == work_list.DIR_REQ)
                            {
                                saveFileDialog.ShowDialog();
                                if (saveFileDialog.FileName.Length > 0)
                                {
                                    save_file_name = saveFileDialog.FileName;
                                    String s;
                                    int pos = save_file_name.LastIndexOf("\\");
                                    s = save_file_name.Substring(pos + 1);
                                    save_file_name_temp = path_temp +"\\"+ s;
                                }
                                //lb_file.Visible = true;
                                pb_file.Visible = true;
                                
                                file_name_event.Set();
                            }
                            break;
                        case work_list.FILE_SEND:
                            if (work_item.Direction == work_list.DIR_IN)
                            {
                                if (save_file_size != 0)
                                {
                                    pb_file.Maximum = save_file_size;
                                    pb_file.Value = (work_item.Seqno * work_list.file_seg_size) % (pb_file.Maximum + 1);
                                }
                                
                                //lb_file.Visible = true;
                                pb_file.Visible = true;
                                //lb_file.Text = (pb_file.Value * 100 / pb_file.Maximum) + "%";
                            }
                            break;
                        case work_list.FILE_ACK:
                            if (work_item.Direction == work_list.DIR_IN)
                            {
                                if (send_file_size != 0)
                                {
                                    pb_file.Maximum = send_file_size;
                                    pb_file.Value = (work_item.Seqno * work_list.file_seg_size) % (pb_file.Maximum + 1);
                                }
                                
                                //lb_file.Visible = true;
                                pb_file.Visible = true;
                                //lb_file.Text = (pb_file.Value * 100 / pb_file.Maximum) + "%";
                            }
                            break;
                        case work_list.FILE_END:
                            if (work_item.Direction == work_list.DIR_IN)
                            {
                                String s;
                                int pos = work_item.Date.LastIndexOf("\\");
                                s = work_item.Date.Substring(pos + 1);
                                String s1 = path_temp + "\\" + s;
                                File.Delete(s1 + ".enc");
                                pb_file.Visible = false;
                                lb_file.Visible = false;
                                log_add_thread("完成向" + work_item.Remote + "发送文件" + send_file_name);
                            }
                            else
                            {
                                String s;
                                int pos = work_item.Date.LastIndexOf("\\");
                                s = work_item.Date.Substring(pos + 1);
                                String s1 = path_temp + "\\" + s;
                                File.Delete(s1 + ".dec");
                                File.Delete(s1 + ".part");
                                File.Delete(s1 + ".temp");
                                pb_file.Visible = false;
                                lb_file.Visible = false;
                                file_name_event.Reset();
                                file_decrypt_key_event.Reset();
                            }
                            break;
                        case work_list.MSG_SEND:
                            if (work_item.Direction == work_list.DIR_IN)
                            {
                                rtb_rec.AppendText("对方  " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
                                rtb_rec.AppendText(work_item.Date + "\r\n");
                                work_item.Oper = work_list.MSG_ACK;
                                work_item.Req_Res = work_list.DIR_RES;
                                work_item.Remote = remote;
                                work_item.Local = local;
                                tcp_out_mutex.WaitOne();
                                tcp_outlist.Enqueue(work_item);
                                tcp_out_mutex.ReleaseMutex();
                            }
                            else
                            {
                                rtb_rec.AppendText("自己  " + DateTime.Now.ToString("HH:mm:ss") + "\r\n");
                                rtb_rec.AppendText(work_item.Date + "\r\n");
                                rtb_send.Clear();
                            }
                            break;

                        case work_list.MSG_ACK:
                            break;
                        case work_list.MISC_LOG:
                            if (work_item.Req_Res == work_list.DIR_REQ)
                            {
                                log_add(work_item.Date);
                            }
                            break;
                        case work_list.MISC_FILE_DEKEY:
                            if (work_item.Req_Res == work_list.DIR_REQ)
                            {

                                Form_Pass frm_pass = new Form_Pass();
                                frm_pass.MessageSent += this.get_decrypt_key;
                                frm_pass.Show();
                            }
                            break;
                        default: break;
                    }
                }
            }
            

        }

        private void get_decrypt_key(String pass)
        {
            save_file_dekey = pass;
            file_decrypt_key_event.Set();
        }
           

        private void btn_sendmsg_Click(object sender, EventArgs e)
        {
            //if (client != null)
            {
                //if (client_connected == true)
                {
                    if (rtb_send.Text.Length == 0)
                    {
                        MessageBox.Show("发送的消息不为空！");
                    }
                    else
                    {
                        work_list item = new work_list();
                        String server = tb_ip.Text.Trim();
                        item.Remote = remote;
                        item.Local = local;
                        item.Seqno = msg_seqno;
                        item.Oper = work_list.MSG_SEND;
                        item.Req_Res = work_list.DIR_REQ;
                        item.Date = rtb_send.Text;
                        tcp_out_mutex.WaitOne();
                        tcp_outlist.Enqueue(item);
                        tcp_out_mutex.ReleaseMutex();
                        msg_seqno++;
                    }
                }
            }
        }

        private void btn_sendfile_Click(object sender, EventArgs e)
        {
            String fileName = tb_file.Text;

            if (File.Exists(fileName) == true)
            {
                work_list item = new work_list();
                item.Req_Res = work_list.DIR_REQ;
                item.Oper = work_list.FILE_START;
                item.Date = tb_pass1.Text + ":" + fileName;
                item.Remote = remote;
                item.Local = local;
                item.Error = work_list.ERROR_OK;
                item.Length = item.Date.Length;
                tcp_out_mutex.WaitOne();
                tcp_outlist.Enqueue(item);
                tcp_out_mutex.ReleaseMutex();
            }
            else
            {
                MessageBox.Show("找不到" + fileName, "找不到文件...");
            }
        }

        private void btn_clearmsg_Click(object sender, EventArgs e)
        {
            rtb_send.Clear();
            rtb_rec.Clear();
        }

        private void btn_listen_Click(object sender, EventArgs e)
        {
            
            if ((tb_port.Text.Trim().Length == 0) || (Convert.ToInt32(tb_port.Text.Trim(), 10) <= 1024))
            {
                MessageBox.Show("请输入要监听的端口（大于1024）。", "设置监听端口", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }
            else
            {
                local_listen_port = (Convert.ToInt32(tb_port.Text.Trim(), 10));
            }
            
            if (btn_listen.Text == "开始监听")
            {
                tb_port.Enabled = false;
                btn_listen.Enabled= false;
                work_list item = new work_list();
                local = local_ip + ":" + local_listen_port;
                item.Local = local;
                item.Oper = work_list.SERVER_START;
                item.Req_Res = work_list.DIR_REQ;
                tcp_out_mutex.WaitOne();
                tcp_outlist.Enqueue(item);
                tcp_out_mutex.ReleaseMutex();
            }
            else
            {
                work_list item = new work_list();
                local = local_ip + ":" + local_listen_port;
                item.Local = local;
                item.Oper = work_list.SERVER_STOP;
                item.Req_Res = work_list.DIR_REQ;
                tcp_out_mutex.WaitOne();
                tcp_outlist.Enqueue(item);
                tcp_out_mutex.ReleaseMutex();
            }
        }

        private void btn_log_Click(object sender, EventArgs e)
        {
            rtb_log.Clear();
            String s = File.ReadAllText(log_file, Encoding.UTF8);
            rtb_log.AppendText(s);
        }

        private void btn_clearlog_Click(object sender, EventArgs e)
        {
            rtb_log.Clear();
        }


    }
}
